<?php
/**
 * I-Cafe framework
 * 
 * @category CF
 * @package CF_Db
 * @link http://www.i-cafe.org
 * @license http://www.i-cafe.org/license
 * @version 04-04-2010
 * @author tcl_java@yahoo.com
 */

/**
 * Provider for query result
 * <code>
 * 	$select = new CF_Db_Select();
 * 	$select->table("products");
 * 	$store = $select->execute();
 * 	$records = $store->getAll();
 * 	$firstRecord = $store->getAt(0);
 * 	$total = $store->getCount();
 * 	$foundRows = $store->foundRows();
 * </code>
 */
class CF_Db_Store extends CF_Component implements Iterator, Countable
{
	/**
	 * @var int
	 */
	protected $_index = 0;
	
	/**
	 * @var resource
	 */
	protected $_result;
	
	/**
	 * @var int
	 */
	protected $fetchMode = MYSQL_ASSOC;
	
	/**
	 * @var int
	 */
	protected $_count = null;
	
	/**
	 * @var int
	 */
	protected $_foundRows = null;
	
	/**
	 * @var int
	 */
	protected $_numfields = null;
	
	/**
	 * @var array
	 */
	protected $_meta = array();
	
	/**
	 * 
	 * @param Resource $result
	 * @return void
	 */
	public function __construct($result)
	{
		# notify before construct event
		$this->fireEvent('beforeconstruct', array($this, $result));
		
		if(is_string($result) || $result instanceof CF_Db_Select)
			$this->_result = CF_Db::getInstance()->query($result, false);
		else
			$this->_result = $result;
			
		# notify construct event
		$this->fireEvent('construct', array($this, $this->_result));
	}
	
	/**
	 * 
	 * @param int $index
	 * @return boolean
	 */
	public function seek($index)
	{
		if($index < 0 || $index >= $this->count()) return false;
		$this->_index = $index;
		return @mysql_data_seek($this->_result, $this->_index);
	}
	
	/**
	 * 
	 * @return int
	 */
	public function count()
	{
		if(null == $this->_count){
			$this->_count = (int) @mysql_num_rows($this->_result);
		}
		return $this->_count;
	}
	
	/**
	 * 
	 * @param boolean $asObject [optional]
	 * @return array|object
	 */
	public function current($asObject = false)
	{
		if($asObject) return @mysql_fetch_object($this->_result);
		$data = @mysql_fetch_array($this->_result, $this->fetchMode);
		
		if(!$data) $data = array();
		return $data;
	}
	
	/**
	 * 
	 * @return int
	 */
	public function key()
	{
		return $this->_index;
	}
	
	/**
	 * 
	 * @return boolean
	 */
	public function next()
	{
		return $this->seek(++$this->_index);
	}
	
	/**
	 * 
	 * @return boolean 
	 */
	public function rewind()
	{
		return $this->seek(0);
	}
	
	/**
	 * 
	 * @return boolean 
	 */
	public function valid()
	{
		return is_array($this->current());
	}
	
	/**
	 * 
	 * @param int $index
	 * @param boolean $asObject [optional]
	 * @return array|object
	 */
	public function getAt($index, $asObject = false)
	{
		if(false == $this->seek($index)) return null;
		return $this->current($asObject);
	}
	
	/**
	 * 
	 * @param boolean $asObject [optional]
	 * @return array|object
	 */
	public function getNext($asObject = false)
	{
		if(false == $this->seek($this->_index + 1)) return null;
		return $this->current($asObject);
	}
	
	/**
	 * 
	 * @param boolean $asObject [optional]
	 * @return array|object
	 */
	public function getPrevious($asObject = false)
	{
		if(false == $this->seek($this->_index - 1)) return null;
		return $this->current($asObject);
	}
	
	/**
	 * 
	 * @param int $start [optional]
	 * @param int $end [optional]
	 * @param boolean $asObject [optional]
	 * @return array
	 */
	public function getRange($start = 0, $end = null, $asObject = false)
	{
		$count = $this->count();
		if(null == $end || $end >= $count) $end = $count - 1;
		
		$res = array();
		for($i = $start; $i <= $end; ++$i){
			$this->seek($i);
			$res[] = $this->current();
		}
		
		return $res;
	}
	
	/**
	 * 
	 * @param boolean $asObject [optional]
	 * @return array
	 */
	public function getAll($asObject = false)
	{
		return $this->getRange(0, null, $asObject);
	}
	
	/**
	 * 
	 * @param int|string $column
	 * @return array
	 */
	public function getColumn($column)
	{
		$records = $this->getAll();
		$data = array();
		foreach($records as $rec){
			$data[] = $rec[$column];
		}
		
		return $data;
	}
	
	/**
	 * 
	 * @param int|string $colKey
	 * @param int|string $colValue
	 * @return 
	 */
	public function getPairs($colKey, $colValue)
	{
		$records = $this->getAll();
		$data = array();
		foreach($records as $rec){
			$data[$rec[$colKey]] = $rec[$colValue];
		}
		
		return $data;
	}
	
	/**
	 * 
	 * @param int|string $row [optional]
	 * @param int|string $col [optional]
	 * @return mixed
	 */
	public function getCell($row = null, $col = null)
	{
		if(is_numeric($row))
			$this->seek($row);
			
		$data = $this->current();
		return $data[$col];
	}
	
	/**
	 * 
	 * @return int
	 */
	public function getNumFields()
	{
		if(null == $this->_numfields){
			$this->_numfields = mysql_num_fields($this->_result);
		}
		
		return $this->_numfields;
	}
	
	/**
	 * 
	 * @return array
	 */
	public function getMeta()
	{
		if(!$this->_meta){
			for($i = 0, $count = $this->getNumFields(); $i < $count; ++$i){
				$this->_meta[] = mysql_fetch_field($this->_result);
			}
		}
		
		return $this->_meta;
	}
	
	/**
	 * 
	 * @return array
	 */
	public function getHeader()
	{
		$meta = $this->getMeta();
		$header = array();
		foreach($meta as $v){
			$header[] = $v['name'];
		}
		
		return $header;		
	}
	
	/**
	 * 
	 * @return int
	 */
	public function getCount()
	{
		return $this->count();
	}
	
	/**
	 * 
	 * @return int
	 */
	public function getFoundRows()
	{
		if(null == $this->_foundRows){
			$query = "SELECT FOUND_ROWS()";
			$this->_foundRows = (int) @mysql_result(CF_Db::getInstance()->query($query, false), 0);
		}
		
		return $this->_foundRows;
	}
	
	/**
	 * 
	 * @return string
	 */
	public function toJson()
	{
		$records = $this->getAll();
		return json_encode($records);
	}
	
	/**
	 * 
	 * @param object $totalKeyName [optional]
	 * @param object $recordKeyName [optional]
	 * @return array
	 */
	public function toExtStore($totalKeyName = 'total', $recordKeyName = 'records')
	{
		$total = $this->getFoundRows();
		$records = $this->getAll();
		
		$data = array(
			$totalKeyName => $total,
			$recordKeyName => $records
		);
		
		return $data;
	}
	
	/**
	 * 
	 * @param string $totalKeyName [optional]
	 * @param string $recordKeyName [optional]
	 * @return string
	 */
	public function toExtJsonStore($totalKeyName = 'total', $recordKeyName = 'records')
	{
		$data = $this->toExtStore();
		return json_encode($data);
	}
}
